#include<stdio.h>
#include<netinet/in.h>//常用定义
#include<sys/socket.h>//常用方法
#include<unistd.h>//提供通用的文件、目录、程序及进程操作的函数
#include<arpa/inet.h>//网络序列转换函数
#include<fcntl.h>//提供对文件控制的函数
#include<sys/epoll.h>//epoll 相关函数
#include<string.h>//memset 等函数
#include<pthread.h>

#include"server.h"
#include"common.h"
#include"session.h"
#include"battle.h"

list_node session_list;
pthread_rwlock_t sl_rw_lock;
unsigned int sockaddr_len;
list_node battle_list;

/*
init() & finish()
*/
void init(){
    session_list=create_list();
    battle_list=create_list();
    pthread_rwlock_init(&sl_rw_lock,NULL);
    sockaddr_len=sizeof(struct sockaddr_in);
}

/*
assist functions
*/

void server_err(Server *server,char *err_str){
    printf("%s\n",err_str);
    destroy_server(server);
}

int is_session_equal(void* o1,void* o2){
    Session* s1=(Session*)o1;
    Session* s2=(Session*)o2;
   if(s1->sockfd==s2->sockfd && 
      s1->notice_sockfd==s2->notice_sockfd)
    {
        return 0;
    }else{
        return -1;
    }
}

int is_battle_equal(void* o1,void *o2){
    Battle *b1=(Battle*)o1;
    Battle *b2=(Battle*)o2;
    if(strncmp(b1->user_name,b2->user_name,USER_NAME_LEN)==0 
    && strncmp(b1->enemy_name,b2->enemy_name,USER_NAME_LEN)==0){
        return 0;
    }else{
        return -1;
    }
}

//从未离线的好友中查找
Session* get_session_by_usrname(char name[USER_NAME_LEN+1]){
    printf("get session by user name!\n");
    list_node temp=session_list;
    while(temp!=NULL && temp->data!=NULL){
        Session* session=(Session*)temp->data;
        if(session->user!=NULL){
            if(strncmp(session->user->name,name,USER_NAME_LEN)==0){
                return session;
            }
        }
        temp=temp->next;
    }
    printf("not find!\n");
    return NULL;
}

void send_usr_list(Session * client){
    list_node temp=session_list;
    while(temp!=NULL && temp->data!=NULL){
        Session* session=(Session*)temp->data;
        User* usr=session->user;
        if(usr!=NULL){
            printf("send user!\n");

            OnlineMsg ol_msg;
            memset(&ol_msg,0,sizeof(OnlineMsg));
            memcpy(ol_msg.name,usr->name,USER_NAME_LEN);
            
            Message msg;
            memcpy(msg.content,&ol_msg,sizeof(OnlineMsg));
            if(send_msg(client->notice_sockfd,msg)==-1){
                printf("send_usr_list:send usr info err!\n");
            }
        }
        temp=temp->next;
    }
}

int find_session(Session* session){
    return find_node(session_list,session,is_session_equal);
}

Battle* get_battle_by_usrname(char name[USER_NAME_LEN+1]){
    list_node temp=battle_list;
    while(temp!=NULL && temp->data!=NULL){
        Battle *battle=(Battle*)temp->data;
        if( (strncmp(battle->user_name,name,USER_NAME_LEN)==0) ||
            (strncmp(battle->enemy_name,name,USER_NAME_LEN)==0)){
                return battle;
            }
        temp=temp->next;
    }
    return NULL;
}

int find_battle(Battle* battle){
    return find_node(battle_list,battle,is_battle_equal);
}

void broadcast(Message msg){
    list_node temp=session_list;
    while(temp!=NULL && temp->data!=NULL){
        Session* session=(Session*)temp->data;
        if(session->user!=NULL){
            printf("broadcast!\n");
            if(send_msg(session->notice_sockfd,msg)==-1){
                printf("broadcast:send err!\n");
            }
        }
        temp=temp->next;
    }
}

void broadcast_online_msg(char name[USER_NAME_LEN+1]){
    OnlineMsg ol_msg;
    memset(&ol_msg,0,sizeof(OnlineMsg));
    memcpy(ol_msg.name,name,USER_NAME_LEN);

    Message msg;
    msg.type=USER_ONLINE_MSG;
    memcpy(msg.content,(void*)&ol_msg,sizeof(OnlineMsg));
    broadcast(msg);
}

void broadcast_offline_msg(char name[USER_NAME_LEN+1]){
    OfflineMsg ofl_msg;
    memset(&ofl_msg,0,sizeof(OfflineMsg));
    memcpy(ofl_msg.name,name,USER_NAME_LEN);

    Message msg;
    msg.type=USER_OFFLINE_MSG;
    memcpy(msg.content,(void*)&ofl_msg,sizeof(OnlineMsg));
    printf("broadcast_offline_msg\n");
    broadcast(msg);
}
/*
 server functions
*/
Session* init_session(const int sockfd){
    Session *session=create_session();
    socklen_t s_len=sockaddr_len;
    session->sockfd=accept(sockfd,(struct sockaddr*)session->user_sock_addr,&s_len);
    make_nonblocking(session->sockfd);
    
    session->user_sock_addr->sin_port=htons(CLIENT_ACEP_PORT);
    session->notice_sockfd=socket(PF_INET,SOCK_STREAM,0);
    if(connect(session->notice_sockfd,(struct sockaddr*)session->user_sock_addr,sockaddr_len)==-1){
        printf("init_session: socket connect err!\n");
        close(session->sockfd);
        close(session->notice_sockfd);
        destory_session(session);
        return NULL;
    }
    printf("client address : %s\n",inet_ntoa(session->user_sock_addr->sin_addr));
    return session;
}

//构造函数
Server* create_server(const char* ip_addr_str,u_int16_t port){
    Server *this=(Server*)calloc(1,sizeof(Server));
    this->is_stoped=0;
    this->sockfd=socket(PF_INET,SOCK_STREAM,0);
    
    if(this->sockfd==-1){
        server_err(this,"server_create:socket create err!");
        return NULL;
    }
    
    struct in_addr ip_addr;
    if(inet_aton(ip_addr_str,&ip_addr)==0){
        server_err(this,"server_create:addr convert err!");
        return NULL;
    }

    struct sockaddr_in sock_addr;
    memset(&sock_addr,0,sizeof(struct sockaddr_in));
    sock_addr.sin_family=AF_INET;
    sock_addr.sin_addr=ip_addr;
    sock_addr.sin_port=htons(port);

    int is_bound=bind(this->sockfd,(struct sockaddr*)&sock_addr,sizeof(struct sockaddr_in));
    if(is_bound!=0){
        server_err(this,"server_create:addr bind err!");
        return NULL;
    }

    int is_listened=listen(this->sockfd,LISTEN_BACK_LOG);
    if(is_listened!=0){
        server_err(this,"server_create:socket listen err!");
        return NULL;
    }
    
    make_nonblocking(this->sockfd);
    return this;
}


//Undone
void handle_exit_msg(ExitMsg e_msg,Session* session){

}

void handle_register_msg(RegisterMsg r_msg,Session* session){
    printf("%s is registering!\n",r_msg.name);

    list_node temp=session_list;
    int is_name_repeated=0;
    int is_session_existed=0;
    while(temp!=NULL && temp->data!=NULL){
        Session* session_temp=(Session*)temp->data;
        if(session_temp->user!=NULL){
            if(strncmp(session_temp->user->name,r_msg.name,USER_NAME_LEN)==0){
                is_name_repeated=1;
                break;
            }if(is_session_equal(session_temp,session)==0){
                is_session_existed=1;
                break;
            }
        }
        temp=temp->next;
    }

    RegisterRpy r_rpy;
    memset(&r_rpy,0,sizeof(RegisterRpy));
    r_rpy.is_successed=0;

    if(is_name_repeated){
        memcpy(r_rpy.info,REGISTER_NAME_REPEATED_INFO,strlen(REGISTER_NAME_REPEATED_INFO));
        printf("reject register:is_session_existed");
    }else if(is_session_existed){
        memcpy(r_rpy.info,REGISTER_SESSION_EXIST_INFO,strlen(REGISTER_SESSION_EXIST_INFO));
        printf("reject register:is_session_existed");
    }else{
        r_rpy.is_successed=1;
        session->user=create_user(r_msg.name);
    }
    
    Message msg;
    msg.type=REGISTER_RPY;
    memcpy(msg.content,&r_rpy,sizeof(RegisterRpy));

    if(send_msg(session->sockfd,msg)==-1){//注册失败，等待下一次注册
        if(session->user!=NULL){
            destory_user(session->user);
            session->user=NULL;
        }
        printf("handle_register_msg:socket send REGISTER_RPY err!\n");
        return ;
    }
    if(r_rpy.is_successed){
        broadcast_online_msg(r_msg.name);
        send_usr_list(session);
    }
}

void handle_makefriend_msg(MakeFriendMsg mf_msg,Session* user_session){
    Session *friend_session=get_session_by_usrname(mf_msg.friend_name);
    Message msg;
    //找不到或者已经添加了就直接拒绝
    if(friend_session==NULL || (find_friend(user_session->user,mf_msg.friend_name)!=-1)){
        MakeFriendRpy mf_rpy;
        memset(&mf_rpy,0,sizeof(MakeFriendRpy));
        mf_rpy.is_success=0;

        if(friend_session==NULL){//找不到在线的好友
            memcpy(mf_rpy.info,USER_OFFLINE_INFO,strlen(USER_OFFLINE_INFO));
        }else{//已经添加过该好友
            memcpy(mf_rpy.info,FRIEND_ADDED_INFO,strlen(FRIEND_ADDED_INFO));
        }

        msg.type=MAKE_FRIEND_REPLY;
        memcpy(msg.content,&mf_rpy,sizeof(MakeFriendRpy));
        if(send_msg(user_session->sockfd,msg)==-1){
            printf("handle_makefriend_msg:send to user err!\n");
        }
    }else{
        msg.type=MAKE_FRIEND_MSG;
        memcpy(msg.content,&mf_msg,sizeof(MakeFriendMsg));
        if(send_msg(friend_session->notice_sockfd,msg)==-1){
            printf("handle_makefriend_msg:send to friend err!\n");
            return ;
        }
    }
}

void handle_makefriend_reply(MakeFriendRpy mf_rpy,Session* friend_session){
    //直接转发即可
    Session *user_session=get_session_by_usrname(mf_rpy.user_name);
    if(user_session!=NULL){
        Message msg;
        msg.type=MAKE_FRIEND_REPLY;
        memcpy(msg.content,&mf_rpy,sizeof(MakeFriendRpy));
        if(send_msg(user_session->sockfd,msg)==-1){
            printf("handle_makefriend_reply:send to user err!\n");
        }
    }
    if(mf_rpy.is_success){
        make_friend(user_session->user,friend_session->user->name);
    }
}

//直接转发即可
void handle_battlerequest_msg(BattleRequestMsg br_msg,Session* user_session){
    printf("handle_battlerequest_msg start!\n");
    Session *enemy_session=get_session_by_usrname(br_msg.enemy_name);
    printf("after get!\n");

    Message msg;
    if(enemy_session==NULL){
        printf("enemy session NULL!");
        BattleRequestRpy br_rpy;
        memset(&br_rpy,0,sizeof(br_rpy));
        br_rpy.is_seccess=0;
        memcpy(br_rpy.info,USER_OFFLINE_INFO,strlen(USER_OFFLINE_INFO));
        
        msg.type=BATTLE_REQUEST_RPY;
        memcpy(msg.content,&br_rpy,sizeof(BATTLE_REQUEST_RPY));
    }else{
        msg.type=BATTLE_REQUEST_MSG;
        memcpy(msg.content,&br_msg,sizeof(BattleRequestMsg));
    }

    if(send_msg(enemy_session->notice_sockfd,msg)==-1){
        printf("handle_battlerequest_msg:send to enemy err!\n");
    }
}

//如果同意则开启连接，如果不同意就转发
void handle_battlerequest_rpy(BattleRequestRpy br_rpy,Session* enemy_session){

    printf("name:%s\n",br_rpy.user_name);
    Session* user_session=get_session_by_usrname(br_rpy.user_name);
    Message msg;
    msg.type=BATTLE_REQUEST_RPY;
    memcpy(msg.content,&br_rpy,sizeof(BattleRequestRpy));
    if(send_msg(user_session->sockfd,msg)==-1){
        printf("handle_battlerequest_rpy:send to user err!\n");
    }
    if(br_rpy.is_seccess==1){
        Battle* battle=create_battle();
        strncpy(battle->user_name,br_rpy.user_name,USER_NAME_LEN);
        strncpy(battle->enemy_name,enemy_session->user->name,USER_NAME_LEN);
        battle_list=add_node(battle_list,(void*)battle);
    }
}

void handle_battle_msg(BattleMsg bt_msg,Session* session){
    Battle *battle=get_battle_by_usrname(bt_msg.user_name);
    Session* enemy_session=NULL;

    if(battle==NULL){
        BattleStopMsg bsp_msg;
        memset(&bsp_msg,0,sizeof(BattleStopMsg));
        memcpy(bsp_msg.stopinfo,BATTLE_CLOSE_INFO,strlen(BATTLE_CLOSE_INFO));

        Message msg;
        msg.type=BATTLE_STOP_MSG;
        memcpy(msg.content,&bsp_msg,sizeof(BattleStopMsg));
        if(send_msg(session->sockfd,msg)==-1){
            printf("handle_battle_msg:send battle stop msg err!\n");
        }
    }else{
        if(action_battle(battle,bt_msg.user_name,bt_msg.action)==1){//需要向双方发送信息
            //当前Session是User
            if(strncmp(session->user->name,battle->user_name,USER_NAME_LEN)==0){
                enemy_session=get_session_by_usrname(battle->enemy_name);
            }else{
                enemy_session=get_session_by_usrname(battle->user_name);
            }

            printf("session user_name:%s\n",session->user->name);
            printf("enemy user_name:%s\n",enemy_session->user->name);

            if(is_battle_ended(battle)){
                BattleResultMsg blrt_msg;
                memset(&blrt_msg,0,sizeof(BattleResultMsg));

                if(is_enemy_lose(battle)){
                    memcpy(blrt_msg.winner,battle->user_name,USER_NAME_LEN+1);
                }else{
                    memcpy(blrt_msg.winner,battle->enemy_name,USER_NAME_LEN+1);
                }

                Message msg;
                msg.type=BATTLE_RESULT_MSG;
                memcpy(msg.content,&blrt_msg,sizeof(BattleResultMsg));
                
                if(send_msg(session->sockfd,msg)==-1){
                    printf("handle_battle_msg:send battle result err!\n");
                }
                if(send_msg(enemy_session->sockfd,msg)==-1){
                    printf("handle_battle_msg:send battle result err!\n");
                }
                //从Battle列表中删除
                int index=find_battle(battle);
                if(index!=-1){
                    battle_list=delete_node(battle_list,index);
                    printf("Battle End!\n");
                }
            }else{
                Message enemy_msg;
                enemy_msg.type=BATTLE_MSG;
                memcpy(enemy_msg.content,&bt_msg,sizeof(BattleMsg));
                if(send_msg(enemy_session->sockfd,enemy_msg)==-1){
                    printf("handle_battle_msg:send to enemy err!\n");
                }
                printf("enemy msg:%s\n",bt_msg.user_name);

                BattleMsg bt_msg1;
                memset(&bt_msg1,0,sizeof(BattleMsg));

                if(strncmp(session->user->name,battle->user_name,USER_NAME_LEN)==0){
                    bt_msg1.action=battle->enemy_action;
                    memcpy(bt_msg1.user_name,battle->enemy_name,USER_NAME_LEN);
                }else{
                    bt_msg1.action=battle->user_action;
                    memcpy(bt_msg1.user_name,battle->user_name,USER_NAME_LEN);
                }

                Message user_msg;
                user_msg.type=BATTLE_MSG;
                memcpy(user_msg.content,&bt_msg1,sizeof(BattleMsg));

                if(send_msg(session->sockfd,user_msg)==-1){
                    printf("handle_battle_msg:send to enemy err!\n");
                }
                printf("user msg:%s\n",bt_msg1.user_name);

                finish_action(battle);
            } 
        } 
    }
}

//转发 并 删除 battle 
void handle_battlestop_msg(BattleStopMsg bsp_msg,Session* session){
    Battle *battle=get_battle_by_usrname(session->user->name);
    Session *another_session=NULL;
    if(strncmp(session->user->name,battle->user_name,USER_NAME_LEN)==0){
        another_session=get_session_by_usrname(battle->enemy_name);
    }else{
        another_session=get_session_by_usrname(battle->user_name);
    }
    Message msg;
    msg.type=BATTLE_STOP_MSG;
    memcpy(msg.content,&bsp_msg,sizeof(BattleStopMsg));
    if(send_msg(another_session->sockfd,msg)==-1){
        printf("handle_battlestop_msg:send battle stop msg err!\n");
    }
    printf("handle_battlestop_msg:finished sending.\n");
}



void handle_msg(Message msg,Session* session){
    printf("handle msg!\n");
    switch(msg.type){
        case EXIT_MSG:
            printf("handle_msg:exit_msg\n");
            break;
        case REGISTER_MSG:{
            printf("handle_msg:register_msg\n");
            RegisterMsg r_msg;
            memcpy(&r_msg,msg.content,sizeof(RegisterMsg));
            handle_register_msg(r_msg,session);
            break;
        }
        case MAKE_FRIEND_MSG:{
            printf("handle_msg:make_friend_msg\n");
            MakeFriendMsg mf_msg;
            memcpy(&mf_msg,msg.content,sizeof(MakeFriendMsg));
            handle_makefriend_msg(mf_msg,session);
            break;
        }
        case MAKE_FRIEND_REPLY:{
            printf("handle_msg:make_friend_rpy\n");
            MakeFriendRpy mf_rpy;
            memcpy(&mf_rpy,msg.content,sizeof(MakeFriendRpy));
            handle_makefriend_reply(mf_rpy,session);
            break;
        }
        case BATTLE_REQUEST_MSG:{
            printf("handle_msg:battle_request_msg\n");
            BattleRequestMsg br_msg;
            memcpy(&br_msg,msg.content,sizeof(BattleRequestMsg));
            handle_battlerequest_msg(br_msg,session);
            break;
        }
        case BATTLE_REQUEST_RPY:{
            printf("handle_msg:battle_request_rpy\n");
            BattleRequestRpy br_rpy;
            memcpy(&br_rpy,msg.content,sizeof(BattleRequestRpy));
            handle_battlerequest_rpy(br_rpy,session);
            break;
        }
        case BATTLE_MSG:{
            printf("handle_msg:battle_msg\n");
            BattleMsg bt_msg;
            memcpy(&bt_msg,msg.content,sizeof(BattleMsg));
            handle_battle_msg(bt_msg,session);
            break;
        }
        case BATTLE_STOP_MSG:{
            printf("handle_msg:battle_stop_msg\n");
            BattleStopMsg bsp_msg;
            memcpy(&bsp_msg,msg.content,sizeof(BattleStopMsg));
            handle_battlestop_msg(bsp_msg,session);
            break;
        }
        case ERR_MSG:{
            printf("handle_msg:err_msg\n");
            break;
        }
        default:
            printf("Unknown msg type err!\n");
            break;
        }
}

//主要过程:核心函数
Server* process_server(Server *this){
    if(this==NULL)return NULL;

    int epfd=epoll_create1(0);
    if(epfd==-1){
        printf("process_server:create epoll err!\n");
        return NULL;
    }
    struct epoll_event event;
    event.data.fd=this->sockfd;
    event.events=EPOLLIN | EPOLLET;

    if(epoll_ctl(epfd,EPOLL_CTL_ADD,this->sockfd,&event)!=0){
        printf("process_server:epoll add listen socket err!\n");
    }

    struct epoll_event* events;
    events=calloc(MAX_EVENTS_NUM,sizeof(event));

    while(!this->is_stoped){
        int n=epoll_wait(epfd,events,MAX_EVENTS_NUM,-1);
        for(int i=0;i<n;i++){
            struct epoll_event temp_event=events[i];
            if((temp_event.events & EPOLLERR) ||
               (temp_event.events & EPOLLHUP) ||
               (temp_event.events & EPOLLRDHUP)){
                if(temp_event.data.fd!=this->sockfd){
                    Session* session=temp_event.data.ptr;
                    broadcast_offline_msg(session->user->name);
                    int index=find_session(session);
                    close(session->sockfd);
                    epoll_ctl(epfd,EPOLL_CTL_DEL,session->sockfd,NULL);
                    close_session(session);
                    destory_session(session);
                    session_list=delete_node(session_list,index);
                }
                continue;
            }
            
            if(temp_event.data.fd==this->sockfd){
                Session* session=init_session(this->sockfd);
                if(session!=NULL){
                    session_list=add_node(session_list,(void*)session);
                    event.data.ptr=session;
                    event.events=EPOLLIN|EPOLLHUP|EPOLLERR;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,session->sockfd,&event);
                }
                continue;
            }

            if(temp_event.events & EPOLLIN){
                char recv_buf[128];
                Session* session=temp_event.data.ptr;
                if(recv(session->sockfd,recv_buf,sizeof(Message),0)==-1){
                    printf("%d\n",session->sockfd);
                    printf("process_server:socket recv err!\n");
                    continue;
                }
                Message msg;
                memcpy(&msg,recv_buf,sizeof(Message));
                handle_msg(msg,temp_event.data.ptr);
            }
       }
    }
    close(epfd);
    return this;
}

//终止其他线程
Server* stop_server(Server *this){
    if(this==NULL)return NULL;
    this->is_stoped=1;
    close(this->sockfd);
    return this;
}


//析构函数
Server* destroy_server(Server *this){
    if(this==NULL)return NULL;
    free(this);
    return NULL;
}

int main(){
    init();
    Server *server=create_server(SERVER_IP_ADDR,SERVER_ACEP_PORT);
    server=process_server(server);
    server=stop_server(server);
    server=destroy_server(server);
    return 0;
}